package base;

import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.Point2D;

/**
 * Staattisia metodeja auttamaan mm. trigonometriassa.
 * 
 * 
 * @author gilead
 *
 */
public class Geometry {

	public static double getX(double headingDeg)
	{
		double headingtrans = Geometry.compassToMathDeg(headingDeg);
		//System.out.println("transformed " + headingDeg + " to " + headingtrans);
		double hrad = Math.toRadians(headingtrans);
		//System.out.println("cos(" + hrad + ")=" + Math.cos(hrad));
		return Math.cos(hrad);
	}
	
	public static double getY(double headingDeg)
	{
		double headingtrans = Geometry.compassToMathDeg(headingDeg);
		//System.out.println("transformed " + headingDeg + " to " + headingtrans);
		double hrad = Math.toRadians(headingtrans);
		//System.out.println("sin(" + hrad + ")=" + Math.sin(hrad));
		return Math.sin(hrad);
	}
	
	public static double mathToCompassDeg(double deg)
	{
		return deg;
		/*
		deg -= 90;
		return Geometry.mirrorHorizontally(deg);
		*/
	}
	
	public static double compassToMathDeg(double deg)
	{
		return deg;
		/*
		double res = deg - 90;
		res = Geometry.mirrorHorizontally(res); 
		//System.out.println(deg + "->" + res);
		return res;
		*/
	}
	
	/**
	 * Returns the direction from one point to another. 
	 * 
	 * @param fromX
	 * @param fromY
	 * @param toX
	 * @param toY
	 * @return
	 */
	public static double getHeading(double fromX, double fromY, double toX, double toY)
	{
		double dx = toX - fromX;
		double dy = toY - fromY;
		//double rad = Math.atan(dy/dx);
		double rad = Math.atan2(dy, dx);
		double cdeg = Geometry.mathToCompassDeg(Math.toDegrees(rad));
		//System.out.println(dy/dx + " -> heading: " + cdeg);
		return Geometry.ensurePositive(cdeg);
	}
	
	public static double ensurePositive(double deg) {
		while (deg < 0) {
			deg += 360;
		}
		while (deg >= 360) {
			deg -= 360;
		}
		return deg;
	}
	
	/**
	 * 
	 * @param deg
	 * @return the mirrored angle, where 0 <= angle < 360
	 */
	public static double mirrorHorizontally(double deg) {
		deg = 360-deg;
		
		return Geometry.ensurePositive(deg);
	}
	
	public static double getDistanceSquared(Point2D.Double from, Point2D.Double to) {
		return getDistanceSquared(from.x, from.y, to.x, to.y);
	}
	
	public static double getDistanceSquared(double fromX, double fromY, double toX, double toY)
	{
		double x = toX - fromX; 
		double y = toY - fromY;
		return  x * x +  y * y; 
	}
	
	/**
	 * For a line that goes from one point to another, solve Y at the given X.
	 * 
	 * @param first first point from the line
	 * @param second second point from the line
	 * @param x the value of x
	 * @return the value of y
	 */
	public static double solveYforLine(Point2D.Double first, Point2D.Double second, double x)
	{
		return first.y + (second.y - first.y) / (second.x - first.x) * (x - first.x);
	}
	
	/**
	 * For a line that goes from one point to another, solve X at the given Y.
	 * 
	 * @param first first point from the line
	 * @param second second point from the line
	 * @param y the value of y
	 * @return the value of x
	 */
	public static double solveXforLine(Point2D.Double first, Point2D.Double second, double y)
	{
		return first.x + (second.x - first.x) / (second.y - first.y) * (y - first.y);
	}
	
	public static Point2D.Double getPoint(double angleDeg, double xd, double yd)
	{
		double rad = Math.toRadians(angleDeg);
		double cos = Math.cos(rad);
		double sin = Math.sin(rad);
		double x = cos * xd - sin * yd;
		double y = cos * yd + sin * xd;
		
		/*
		angleDeg = Geometry.ensurePositive(angleDeg);
		if (angleDeg > 90 && angleDeg < 270)
		{
			x *= -1;
		}
		if (angleDeg > 180 && angleDeg < 360)
		{
			y *= -1;
		}
		*/
		
		return new Point2D.Double(x,y);
	}
	
	public static Point2D.Double getPoint(double angleDeg, double xd, double yd, double xoffset, double yoffset)
	{
		Point2D.Double result = getPoint(angleDeg, xd, yd);
		result.x += xoffset;
		result.y += yoffset;
		return  result;
	}
	
	/**
	 * Returns a rectangle with given width and height that is rotated with given angle
	 * 
	 * The indexes go as follows:
	 * 0 upper left
	 * 1 lower left
	 * 2 lower right
	 * 3 upper right
	 * 
	 * @param width
	 * @param height
	 * @param angleDeg
	 * @return
	 */
	public static Point2D.Double [] rotateRect(double width, double height, double angleDeg)
	{
		double rad = Math.toRadians(angleDeg);
		double cos = Math.cos(rad);
		double sin = Math.sin(rad);
		
		Point2D.Double [] result = new Point2D.Double [4];
		
		double x1 = height / 2 * cos;
		double x2 = width / 2 * sin;
		double y1 = width / 2 * cos;
		double y2 = height / 2 * sin;
		
		result[0] = new Point2D.Double( x1 - x2,  y1 + y2);
		result[1] = new Point2D.Double(-x1 - x2, -y1 + y2);
		result[2] = new Point2D.Double(-x1 + x2, -y1 - y2);
		result[3] = new Point2D.Double( x1 + x2,  y1 - y2);
		
		return result;
	}
	
	
	/**
	 * 
	 * The indexes go as follows:
	 * 0 upper left
	 * 1 lower left
	 * 2 lower right
	 * 3 upper right
	 * 
	 * @param point
	 * @param rect
	 * @return
	 */
	
	public static boolean intersectsRect(Point2D.Double [] a, Point2D.Double [] b)
	{
		// create the lines of the rectangle
		Line2D.Double[] la = Geometry.getRectLines(a);
		Line2D.Double[] lb = Geometry.getRectLines(b);
		
		for(int i=0; i < 4; i++) {
			for (int j=0; j < 4; j++) {
				if (la[i].intersectsLine(lb[j])) {
					// if any of the lines intersect, return true
					return true;
				}
			}
		}
		
		return false;
	}
	
	/**
	 * Turns the given rectangle points to lines
	 * 
	 * @return
	 */
	public static Line2D.Double[] getRectLines(Point2D.Double [] a)
	{
		Line2D.Double[] la = new Line2D.Double[4];
		
		for (int i=0; i < 3; i++) {
			la[i] = new Line2D.Double(a[i], a[i+1]);
		}
		la[3] = new Line2D.Double(a[3], a[0]);
		
		return la;
	}
	
	public static Point2D.Double getMin(Point2D.Double[] a)
	{
		if (a.length == 0) {
			return new Point2D.Double(Double.NaN, Double.NaN);
		}
		Point2D.Double min = new Point2D.Double(Double.MAX_VALUE, Double.MAX_VALUE);
		
		for (Point2D.Double p : a)
		{
			if (p.x < min.x) {
				min.x = p.x;
			}
			if (p.y < min.y) {
				min.y = p.y;
			}
		}
		
		return min;
	}
	
	public static Point2D.Double getMax(Point2D.Double[] a)
	{
		if (a.length == 0) {
			return new Point2D.Double(Double.NaN, Double.NaN);
		}
		
		Point2D.Double max = new Point2D.Double(Double.MIN_VALUE, Double.MIN_VALUE);
		
		for (Point2D.Double p : a)
		{
			if (p.x > max.x) {
				max.x = p.x;
			}
			if (p.y > max.y) {
				max.y = p.y;
			}
		}
		
		return max;
	}
}
